﻿
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.XPath;


// http://stackoverflow.com/questions/3151450/google-geolocation-api-use-longitude-and-latitude-to-get-address

namespace ReverseGeoLookup
{
    class ReverseGeoLookup
    {
        //<GeocodeResponse>
        //<status>OVER_QUERY_LIMIT</status>
        //</GeocodeResponse>
        // http://stackoverflow.com/questions/2419219/how-do-i-geocode-20-addresses-without-receiving-a-over-query-limit-response
        // http://code.google.com/apis/maps/articles/geocodestrat.html
        // http://code.google.com/apis/maps/documentation/geocoding/index.html#Limits

        // http://code.google.com/apis/maps/articles/geocodestrat.html

        // 	-40.402864	-20.342288	0.0	0.0	BR-262 - Pistal Lateral - Vila Capixaba, Cariacica - ES, Brazil
        // -40.288124	-20.355536	0.0	0.0	R. Natal, 1-61 - Praia de Itapoã, Vila Velha - ES, 29101-710, Brazil
        // http://code.google.com/apis/maps/documentation/geocoding/#ReverseGeocoding
        //http://maps.google.com/maps/api/geocode/xml?latlng=40.714224,-73.961452&sensor=false
        public static string ReverseGeoLoc(string longitude, string latitude,
            out string Address_ShortName,
            out string Address_country,
            out string Address_administrative_area_level_1,
            out string Address_administrative_area_level_2,
            out string Address_administrative_area_level_3,
            out string Address_colloquial_area,
            out string Address_locality,
            out string Address_sublocality,
            out string Address_neighborhood,
            out string Street_address)
        {

            Address_ShortName = "";
            Address_country = "";
            Address_administrative_area_level_1 = "";
            Address_administrative_area_level_2 = "";
            Address_administrative_area_level_3 = "";
            Address_colloquial_area = "";
            Address_locality = "";
            Address_sublocality = "";
            Address_neighborhood = "";
            Street_address = "";

            XmlDocument doc = new XmlDocument();

            try
            {
                //http://maps.googleapis.com/maps/api/geocode/xml?latlng=-20.3558483333333,-40.288615&sensor=false
                doc.Load("http://maps.googleapis.com/maps/api/geocode/xml?latlng=" + latitude + "," + longitude + "&sensor=false");
                XmlNode element = doc.SelectSingleNode("//GeocodeResponse/status");

                //http://code.google.com/apis/maps/documentation/geocoding/index.html#Limits
                //Status Codes

                //The "status" field within the Geocoding response object contains the status of the request, and may contain debugging information to help you track down why Geocoding is not working. The "status" field may contain the following values:

                //    "OK" indicates that no errors occurred; the address was successfully parsed and at least one geocode was returned.
                //    "ZERO_RESULTS" indicates that the geocode was successful but returned no results. This may occur if the geocode was passed a non-existent address or a latlng in a remote location.
                //    "OVER_QUERY_LIMIT" indicates that you are over your quota.
                //    "REQUEST_DENIED" indicates that your request was denied, generally because of lack of a sensor parameter.
                //    "INVALID_REQUEST" generally indicates that the query (address or latlng) is missing.

                if (element.InnerText == "ZERO_RESULTS")
                {
                    return ("No data available for the specified location");
                }
                else if(element.InnerText=="OVER_QUERY_LIMIT")
                {
                    string errorCode = "Excedeu limite de consultas diárias no Google (OVER_QUERY_LIMIT)";
                    Address_ShortName = errorCode;
                    Address_country = errorCode;
                    Address_administrative_area_level_1 = "";
                    Address_administrative_area_level_2 = "";
                    Address_administrative_area_level_3 = "";
                    Address_colloquial_area = "";
                    Address_locality = "";
                    Address_sublocality = "";
                    Address_neighborhood = "";
                    Street_address = errorCode;
                    return ("Excedeu limite de consultas diárias no Google (OVER_QUERY_LIMIT)");
                }
                else
                {

                    element = doc.SelectSingleNode("//GeocodeResponse/result/formatted_address");
                    Street_address = element.InnerText;

                    string longname = "";
                    string shortname = "";
                    string typename = "";
                    bool fHit = false;


                    XmlNodeList xnList = doc.SelectNodes("//GeocodeResponse/result/address_component");
                    foreach (XmlNode xn in xnList)
                    {
                        try
                        {
                            longname = xn["long_name"].InnerText;
                            shortname = xn["short_name"].InnerText;
                            typename = xn["type"].InnerText;


                            fHit = true;
                            switch (typename)
                            {
                                //Add whatever you are looking for below
                                case "country":
                                    {
                                        Address_country = longname;
                                        Address_ShortName = shortname;
                                        break;
                                    }

                                case "locality":
                                    {
                                        Address_locality = longname;
                                        //Address_locality = shortname; //Om Longname visar sig innehålla konstigheter kan man använda shortname istället
                                        break;
                                    }

                                case "sublocality":
                                    {
                                        Address_sublocality = longname;
                                        break;
                                    }

                                case "neighborhood":
                                    {
                                        Address_neighborhood = longname;
                                        break;
                                    }

                                case "colloquial_area":
                                    {
                                        Address_colloquial_area = longname;
                                        break;
                                    }

                                case "administrative_area_level_1":
                                    {
                                        Address_administrative_area_level_1 = longname;
                                        break;
                                    }

                                case "administrative_area_level_2":
                                    {
                                        Address_administrative_area_level_2 = longname;
                                        break;
                                    }

                                case "administrative_area_level_3":
                                    {
                                        Address_administrative_area_level_3 = longname;
                                        break;
                                    }

                                default:
                                    fHit = false;
                                    break;
                            }


                            if (fHit)
                            {
                                Console.Write(typename);
                                Console.ForegroundColor = ConsoleColor.Green;
                                Console.Write("\tL: " + longname + "\tS:" + shortname + "\r\n");
                                Console.ForegroundColor = ConsoleColor.Gray;
                            }
                        }

                        catch (Exception e)
                        {
                            //Node missing either, longname, shortname or typename
                            fHit = false;
                            Console.Write(" Invalid data: ");
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.Write("\tX: " + xn.InnerXml + "\r\n");
                            Console.ForegroundColor = ConsoleColor.Gray;
                        }


                    }

                    //Console.ReadKey();
                    return (element.InnerText);
                }

            }
            catch (Exception ex)
            {
                return ("(Address lookup failed: ) " + ex.Message);
            }
        }
    }

}


//<GeocodeResponse> 
// <status>OK</status> 
// <result> 
//  <type>street_address</type> 
//  <formatted_address>277 Bedford Ave, Brooklyn, NY 11211, USA</formatted_address> 
//  <address_component> 
//   <long_name>277</long_name> 
//   <short_name>277</short_name> 
//   <type>street_number</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>Bedford Ave</long_name> 
//   <short_name>Bedford Ave</short_name> 
//   <type>route</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>Brooklyn</long_name> 
//   <short_name>Brooklyn</short_name> 
//   <type>sublocality</type> 
//   <type>political</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>New York</long_name> 
//   <short_name>New York</short_name> 
//   <type>locality</type> 
//   <type>political</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>Kings</long_name> 
//   <short_name>Kings</short_name> 
//   <type>administrative_area_level_2</type> 
//   <type>political</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>New York</long_name> 
//   <short_name>NY</short_name> 
//   <type>administrative_area_level_1</type> 
//   <type>political</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>United States</long_name> 
//   <short_name>US</short_name> 
//   <type>country</type> 
//   <type>political</type> 
//  </address_component> 
//  <address_component> 
//   <long_name>11211</long_name> 
//   <short_name>11211</short_name> 
//   <type>postal_code</type> 
//  </address_component> 
//  <geometry> 
//   <location> 
//    <lat>40.7142330</lat> 
//    <lng>-73.9612910</lng> 
//   </location> 
//   <location_type>ROOFTOP</location_type> 
//   <viewport> 
//    <southwest> 
//     <lat>40.7110854</lat> 
//     <lng>-73.9644386</lng> 
//    </southwest> 
//    <northeast> 
//     <lat>40.7173806</lat> 
//     <lng>-73.9581434</lng> 
//    </northeast> 
//   </viewport> 
//  </geometry> 
// </result> 
//</GeocodeResponse> 